package com.fwtai.pay.wechat.sp;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fwtai.pay.wechat.WechatPayHttpClientBuilder;
import com.fwtai.pay.wechat.auth.PrivateKeySigner;
import com.fwtai.pay.wechat.auth.Verifier;
import com.fwtai.pay.wechat.auth.WechatPay2Credentials;
import com.fwtai.pay.wechat.auth.WechatPay2Validator;
import com.fwtai.pay.wechat.cert.CertificatesManager;
import com.fwtai.pay.wechat.util.AesUtil;
import com.fwtai.pay.wechat.util.PemUtil;
import com.fwtai.tool.IdBuilder;
import com.fwtai.tool.ToolString;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;

/**
 * 服务商模式的支付工具类
 * todo 在服务商模式下的子商户是否参与分账分钱需要以下步骤；
 * 1.需要在‘服务商平台->服务商功能->特约商户申请-新增商户->状态显示为已完成后->
 * 2.再到服务商平台->产品中心->特约商户授权产品->服务商分账->开通->从`特约商户名称|商户号`	发起邀请后的授权状态为已授权才有效’
 * 3.在基础支付中上传参数 profit_sharing，是否指定分账，当请求支付。支付完成后，调用请求分账(https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_1.shtml)接口，完成分账
 * 4.说明：实现分账只是在普通支付下单接口中新增了一个分账参数 profit_sharing，其他与普通支付方式完全相同。目前支持付款码支付、JSAPI支付、Native支付、APP支付、小程序支付、H5支付、委托代扣。
 * @作者 田应平
 * @版本 v1.0
 * @创建时间 2022/7/8 19:52
 * @QQ号码 444141300
 * @Email service@yinlz.com
 * @官网 <url>http://www.fwtai.com</url>
*/
public final class ToolSPWechat{

  private static final String url_order_create = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi";
  private static final String url_receivers_add = "https://api.mch.weixin.qq.com/v3/profitsharing/receivers/add";
  private static final String url_receivers_delete = "https://api.mch.weixin.qq.com/v3/profitsharing/receivers/delete";
  private static final String url_profit_share = "https://api.mch.weixin.qq.com/v3/profitsharing/orders";

  private ToolSPWechat(){}

  //获取验证器
  private static Verifier getVerifier() throws Exception{
    final PrivateKey privateKey = PemUtil.loadPrivateKey(ToolSPConstants.PRIVATE_KEY);
    // 获取证书管理器实例
    final CertificatesManager certificatesManager = CertificatesManager.getInstance();
    // 向证书管理器增加需要自动更新平台证书的商户信息
    certificatesManager.putMerchant(
      ToolSPConstants.SP_MCH_ID,
      new WechatPay2Credentials(ToolSPConstants.SP_MCH_ID,new PrivateKeySigner(ToolSPConstants.MCH_SERIAL_NO,privateKey)),
      ToolSPConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8));
    // 从证书管理器中获取verifier
    return certificatesManager.getVerifier(ToolSPConstants.SP_MCH_ID);
  }

  private static CloseableHttpClient getHttpClient() throws Exception{
    final Verifier verifier = getVerifier();
    final PrivateKey privateKey = PemUtil.loadPrivateKey(ToolSPConstants.PRIVATE_KEY);
    // 构造httpclient
    return WechatPayHttpClientBuilder.create().withMerchant(ToolSPConstants.SP_MCH_ID, ToolSPConstants.MCH_SERIAL_NO,privateKey).withValidator(new WechatPay2Validator(verifier)).build();
  }

  /**
   * 定义下单,生成预支付参数,先判断是否为空再做业务处理
   * 用于生成获取 prepay_id 的参数组装
   * @param sub_mchid 子商户的商户号，由微信支付生成并下发。(必须在商户模式下的‘服务商功能’->特约商户申请->新增商户)
   * @param sub_openid 用户子标识（付款用户的openId）
   * @param out_trade_no 商户订单号,商户系统内部订单号
   * @param description 商品描述,body
   * @param notify_url 通知地址
   * @param total 总金额
   * @param profit_share 是否指定分账，true：是 false：否
   * @作者 田应平
   * @QQ 444141300
   * @创建时间 2022/7/8 19:49
  */
  public static HashMap<String,String> getParamsOrder(final String sub_mchid,final String sub_openid,final String out_trade_no,final String description,final String notify_url,final Integer total,final boolean profit_share,final String... attach)throws Exception{
    final String prepayId = getpayIdOrder(sub_mchid, sub_openid, out_trade_no, description, notify_url, total,profit_share,attach);
    final JsonNode node = new ObjectMapper().readTree(prepayId);
    final JsonNode jsonNode = node.get("code");
    if(jsonNode != null){
      final String code = jsonNode.asText();
      if(code != null){
        final HashMap<String,String> map = new HashMap<>(2);
        map.put("fail",code);
        map.put("message",node.get("message").asText());
        return map;
      }
    }
    final String prepayValue = node.get("prepay_id").asText();
    final String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
    return initParam(timestamp,prepayValue);
  }

  //获取‘预支付交易会话标识’,todo 官方的api文档只给：请求URL、请求参数、请求示例、返回参数、返回示例以及错误码，剩余的自己脑补（文档地址 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml）
  private static String getpayIdOrder(final String sub_mchid,final String sub_openid,final String out_trade_no,final String description,final String notify_url,final Integer total,final boolean profit_share,final String... attach) throws Exception{
    final HttpPost httpPost = getHttpPost(url_order_create,getParamOrder(sub_mchid,sub_openid, out_trade_no, description, notify_url, total,profit_share,attach));
    final CloseableHttpResponse response = getHttpClient().execute(httpPost);
    return EntityUtils.toString(response.getEntity());
  }

  // 初始化支付参数
  private static HashMap<String,String> initParam(final String timestamp,final String prepayValue){
    final HashMap<String,String> map = new HashMap<>(6);
    final String nonceStr = String.valueOf(new IdBuilder().getId121L());
    map.put("appId",ToolSPConstants.SP_APPID);//若是服务商模式的话则在`服务商平台`下的‘产品中心’->‘AppID账号管理’->‘+关联AppID’ 关联即可（可以不是同一个企业名）；若是商户模式的话则是在`商户平台`的‘产品中心’->‘AppID账号管理’->‘+关联AppID’ 关联即可；
    map.put("timeStamp",timestamp);
    map.put("nonceStr",nonceStr);
    map.put("package","prepay_id="+prepayValue);//示例值：prepay_id=wx201410272009395522657a690389285100
    map.put("signType","RSA");//签名类型，默认为RSA，仅支持RSA
    map.put("paySign",getPaySign(timestamp,nonceStr,prepayValue));
    return map;
  }

  //签名方法,使用字段appId、timeStamp、nonceStr、package（示例值：prepay_id=wx201410272009395522657a690389285100）；小程序或app的签名是一致的,需要注意的是小程序的是 prepay_id=xxxxx，而app都没有prepay_id=，即直接就是 xxxxx
  public static String getPaySign(final String timestamp,final String nonceStr,final String prepayValue){
    final StringBuilder builder = new StringBuilder();
    //服务商模式时使用特约商户的应用appId[服务商和特约商户(子商户)的应用主体不一致时使用]
    builder.append(ToolSPConstants.SUB_APPID).append("\n");//每行都要拼接\n换行符,
    //时间戳
    builder.append(timestamp).append("\n");//每行都要拼接\n换行符
    //随机字符串（nonceStr）
    builder.append(nonceStr).append("\n");//每行都要拼接\n换行符
    //订单详情扩展字符串|预支付交易会话的id|prepay_id
    builder.append("prepay_id=").append(prepayValue).append("\n");//每行都要拼接\n换行符
    return sign(builder.toString().getBytes(StandardCharsets.UTF_8),ToolSPConstants.PRIVATE_KEY);
  }

  //加密
  public static String sign(final byte[] message,final String private_key){
    try {
      final Signature sign = Signature.getInstance("SHA256withRSA");
      final PrivateKey privateKey = PemUtil.loadPrivateKey(private_key);
      sign.initSign(privateKey);
      sign.update(message);
      return Base64.getEncoder().encodeToString(sign.sign());
    } catch (NoSuchAlgorithmException e) {
      throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
    } catch (SignatureException e) {
      throw new RuntimeException("签名计算失败", e);
    } catch (InvalidKeyException e) {
      throw new RuntimeException("无效的私钥", e);
    }
  }

  //接收成功时应答,final
  public static String success(){
    return "{\"code\": \"SUCCESS\",\"message\": \"接收成功\"}";
  }

  //接收成功但处理业务失败时应答,final
  public static String failure(){
    return "{\"code\": \"FAIL\",\"message\": \"失败\"}";
  }

  /**
   * 获取POST请求HttpPost
   * @param entity POST请求时不能为空
   * @作者 田应平
   * @QQ 444141300
   * @创建时间 2022/7/21 14:29
  */
  private static HttpPost getHttpPost(final String url,final HttpEntity entity){
    final HttpPost httpPost = new HttpPost(url);
    httpPost.addHeader("Accept", "application/json");
    httpPost.addHeader("Content-type","application/json; charset=utf-8");
    httpPost.setEntity(entity);
    return httpPost;
  }

  /**
   * 获取GET请求HttpGet
   * @作者 田应平
   * @QQ 444141300
   * @创建时间 2022/7/21 14:29
  */
  private static HttpGet getHttpGet(final String url){
    final HttpGet httpGet = new HttpGet(url);
    httpGet.addHeader("Accept", "application/json");
    httpGet.addHeader("Content-type","application/json; charset=utf-8");
    return httpGet;
  }

  //查询子商户最大分账比例
  public static String getShareConfigs(final String mchid) throws Exception{
    final HttpGet httpGet = getHttpGet("https://api.mch.weixin.qq.com/v3/profitsharing/merchant-configs/" + mchid);
    final CloseableHttpResponse response = getHttpClient().execute(httpGet);
    return EntityUtils.toString(response.getEntity());
  }

  //哪一个参数不为空时就返回哪一个
  public static String getValue(final String... values){
    for(int i = values.length - 1; i >= 0; i--){
      final String value = values[i];
      if(value != null) return value;
    }
    return null;
  }

  /**
   * 用于生成获取 prepay_id 的参数组装[服务商和特约商户(子商户)的应用主体不一致时使用]
   * @param sub_mchid 子商户的商户号，由微信支付生成并下发。(必须在商户模式下的‘服务商功能’->特约商户申请->新增商户)
   * @param sub_openid 用户子标识（付款用户的openId）
   * @param out_trade_no 商户订单号,商户系统内部订单号
   * @param description 商品描述,body
   * @param notify_url 通知地址
   * @param total 总金额
   * @param profit_share 是否指定分账，true：是 false：否
   * @作者 田应平
   * @QQ 444141300
   * @创建时间 2022/7/8 19:49
  */
  private static StringEntity getParamOrder(final String sub_mchid,final String sub_openid,final String out_trade_no,final String description,final String notify_url,final Integer total,final boolean profit_share,final String... attach) throws IOException {
    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
    final ObjectMapper objectMapper = new ObjectMapper();
    final ObjectNode rootNode = objectMapper.createObjectNode();
    rootNode.put("sp_appid",ToolSPConstants.SP_APPID)//在[服务商和特约商户(子商户)的应用主体不一致时使用]已更改过
      .put("sub_appid",ToolSPConstants.SUB_APPID)
      .put("sp_mchid",ToolSPConstants.SP_MCH_ID)
      .put("sub_mchid",sub_mchid)
      .put("description",description)
      .put("out_trade_no",out_trade_no)
      .put("notify_url",notify_url)
      .put("attach",handlerArrs(attach));
    rootNode.putObject("amount").put("total",total);//单位：分钱，需要x100才是64位的正整数
    rootNode.putObject("settle_info").put("profit_sharing",profit_share);//是否指定分账，true：是 false：否,分账只是在原来的基础上附加这个属性即可
    rootNode.putObject("payer").put("sub_openid",sub_openid);//小程序下的用户的openId。 下单前需获取到用户的Openid（用户在子商户appid下的唯一标识。若传sub_openid，那sub_appid必填。）此处用的特约商户的openId
    objectMapper.writeValue(bos,rootNode);
    return new StringEntity(bos.toString("UTF-8"),StandardCharsets.UTF_8);
  }

  //将数组转字符串,以逗号隔开
  private static String handlerArrs(final String... array){
    final StringBuilder sb = new StringBuilder();
    // 遍历数组，将元素添加到StringBuilder对象中
    for (int i = 0; i < array.length; i++) {
      sb.append(array[i]);
      if (i < array.length - 1) {
        sb.append(",");
      }
    }
    return sb.toString();
  }

  /**
   * 拼装添加分账接收方
   * @param sub_mchid 微信支付分配的子商户号，即分账的出资商户号。
   * @param receiveType 分账接收方类型,和下面的account对应,MERCHANT_ID：商户ID或PERSONAL_OPENID：个人openid
   * @param account 分账接收方账号,和上面的receiveType对应,类型是MERCHANT_ID时，是商户号 类型是PERSONAL_OPENID时，是个人openid
   * @return StringEntity
  */
  private static StringEntity getAddReceivers(final String sub_mchid,final String receiveType,final String account) throws IOException {
    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
    final ObjectMapper objectMapper = new ObjectMapper();
    final ObjectNode rootNode = objectMapper.createObjectNode();
    rootNode.put("appid",ToolSPConstants.SP_APPID)//若有问题替换,可参考方法当前方法 getParamOrder()
      .put("sub_mchid",sub_mchid)
      .put("type",receiveType)
      .put("account",account)
      .put("relation_type","PARTNER");//统一为合作伙伴
    objectMapper.writeValue(bos, rootNode);
    return new StringEntity(bos.toString("UTF-8"),StandardCharsets.UTF_8);
  }

  /**
   * 拼装删除分账接收方
   * @param sub_mchid 微信支付分配的子商户号，即分账的出资商户号。
   * @param receiveType 分账接收方类型,和下面的account对应,MERCHANT_ID：商户ID或PERSONAL_OPENID：个人openid
   * @param account 分账接收方账号,和上面的receiveType对应,类型是MERCHANT_ID时，是商户号 类型是PERSONAL_OPENID时，是个人openid
   * @return StringEntity
  */
  private static StringEntity getDelReceivers(final String sub_mchid,final String receiveType,final String account) throws IOException {
    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
    final ObjectMapper objectMapper = new ObjectMapper();
    final ObjectNode rootNode = objectMapper.createObjectNode();
    rootNode.put("appid",ToolSPConstants.SP_APPID)
      .put("sub_mchid",sub_mchid)
      .put("type",receiveType)
      .put("account",account);
    objectMapper.writeValue(bos, rootNode);
    return new StringEntity(bos.toString("UTF-8"),StandardCharsets.UTF_8);
  }

  /**
   * 拼装请求分账,服务商代特约商户发起分账请求，将结算后的资金分到分账接收方;请求分账到各个接收方（对同一笔订单最多能发起50次分账请求，每次请求最多分给50个接收方）
   * @param sub_mchid 子商户号，即分账的出资商户号。
   * @param transaction_id 微信支付订单号
   * @param out_order_no 商户分账单号
   * @param unfreeze_unsplit 是否解冻剩余未分资金：如果为true，该笔订单剩余未分账的金额会解冻回分账方商户；如果为false，该笔订单剩余未分账的金额不会解冻回分账方商户，可以对该笔订单再次进行分账。
   * @param receivers list分账接收方列表,其中的key为 type(String,分账接收方类型)、account(String,分账接收方账号)、amount(Integer,分账金额,单位为分，只能为整数,需要x100)、description(String，分账描述)
  */
  private static StringEntity getSubAccount(final String sub_mchid,final String transaction_id,final String out_order_no,final ArrayList<HashMap<String,Object>> receivers,final boolean unfreeze_unsplit) throws IOException{
    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
    final ObjectMapper objectMapper = new ObjectMapper();
    final ObjectNode rootNode = objectMapper.createObjectNode();
    rootNode.put("appid",ToolSPConstants.SP_APPID)
      .put("sub_mchid",sub_mchid)
      .put("transaction_id",transaction_id)
      .put("out_order_no",out_order_no)
      .put("unfreeze_unsplit",unfreeze_unsplit)
      .replace("receivers",listToArray(receivers))
    ;
    objectMapper.writeValue(bos, rootNode);
    return new StringEntity(bos.toString("UTF-8"),StandardCharsets.UTF_8);
  }

  private static ArrayNode listToArray(final ArrayList<HashMap<String,Object>> receivers){
    final ArrayNode jsonNodes = new ObjectMapper().getNodeFactory().arrayNode();
    for(int i = receivers.size() - 1; i >= 0; i--){
      final HashMap<String,Object> map = receivers.get(i);
      final ObjectNode rootNode = new ObjectMapper().createObjectNode();
      for(final String key : map.keySet()){
        final Object object = map.get(key);
        if(object instanceof Integer){
          rootNode.put(key,(Integer) object);
        } else {
          rootNode.put(key,(String) object);
        }
      }
      jsonNodes.add(rootNode);
    }
    return jsonNodes;
  }

  //通知回调-验签
  public static boolean signVerify(final String serial,final String message,final String signature) throws Exception{
    return getVerifier().verify(serial,message.getBytes(StandardCharsets.UTF_8),signature);
  }

  //通知回调-解密
  public static String decrypt(final String body) throws JsonProcessingException, GeneralSecurityException{
    final AesUtil aesUtil = new AesUtil(ToolSPConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8));
    final ObjectMapper objectMapper = new ObjectMapper();
    final JsonNode node = objectMapper.readTree(body);
    final JsonNode resource = node.get("resource");
    final String ciphertext = resource.get("ciphertext").asText();
    final String associatedData = resource.get("associated_data").asText();
    final String nonce = resource.get("nonce").asText();
    return aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),nonce.getBytes(StandardCharsets.UTF_8),ciphertext);
  }

  /**获取微信服务器的支付成功通知参数,todo 必须验证非空再业务处理*/
  public static String getNotifyInfo(final HttpServletRequest request) throws Exception{
    final BufferedReader br = request.getReader();//获取 应答主体（response Body），需要按照接口返回的顺序进行验签，错误的顺序将导致验签失败。
    String str = "";
    final StringBuilder builder = new StringBuilder();
    while((str = br.readLine()) != null){
      builder.append(str);
    }
    final String signStr = request.getHeader("Wechatpay-Timestamp") + "\n" + request.getHeader("Wechatpay-Nonce") + "\n" + builder + "\n";
    //1.验证签名 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_1.shtml
    final String serial = request.getHeader("Wechatpay-Serial");
    final String signature = request.getHeader("wechatpay-signature");
    if(serial == null || signature == null){
      return null;
    }
    if(!signVerify(serial,signStr,signature)){
      return null;
    }
    //2.解密报文
    return decrypt(builder.toString());
  }

  //服务商模式的获取通知参数,支付附加参数[服务商和特约商户(子商户)的应用主体不一致时使用]
  public static HashMap<String,String> extractParam(final String notifyInfo){
    final HashMap<String,String> map = ToolString.parseJsonObject(notifyInfo);
    final String openid = ToolString.parseJsonObject(map.get("payer")).get("sub_openid");
    final String total_fee = ToolString.parseJsonObject(map.get("amount")).get("total");//total是总金额;payer_total是实际支付的金额.若实际支付的金额不等于总金额时需要后台处理
    final HashMap<String,String> params = new HashMap<>(6);
    params.put("out_trade_no",map.get("out_trade_no"));
    params.put("mchid",map.get("sub_mchid"));//服务商模式下的子商户号
    params.put("transaction_id",map.get("transaction_id"));
    params.put("attach",map.get("attach"));
    params.put("total_fee",String.valueOf(Double.parseDouble(total_fee)/100));//支付时x100,此处要/100
    params.put("openid",openid);
    return params;
  }

  /**
   * 添加分账接收方(todo 订单的分账关系？设计分账关系的表)
   * @param sub_mchid 微信支付分配的子商户号，即分账的出资商户号。
   * @param receiveType 分账接收方类型,和下面的account对应,MERCHANT_ID：商户ID或PERSONAL_OPENID：个人openid
   * @param account 分账接收方账号,和上面的receiveType对应,类型是MERCHANT_ID时，是商户号 类型是PERSONAL_OPENID时，是个人openid
   * @return StringEntity
  */
  private static String addReceivers(final String sub_mchid,final String receiveType,final String account) throws Exception{
    final HttpPost httpPost = getHttpPost(url_receivers_add,getAddReceivers(sub_mchid,receiveType,account));
    final CloseableHttpResponse response = getHttpClient().execute(httpPost);
    return EntityUtils.toString(response.getEntity());
  }

  /**
   * 添加分账接收方为商户类型
   * @param sub_mchid 微信支付分配的子商户号，即分账的出资商户号。
   * @param mchid 分账方为商户类型时的商户号,可以和‘分账的出资商户号’一致，表示是出资商户号也参与分账
  */
  public static String addMerchantReceiver(final String sub_mchid,final String mchid) throws Exception{
    return addReceivers(sub_mchid,"MERCHANT_ID",mchid);
  }

  /**
   * 添加分账接收方为个人类型（可以避免收税）
   * @param sub_mchid 微信支付分配的子商户号，即分账的出资商户号。
   * @param openid 分账方为个人时的openId
  */
  public static String addPersonalReceiver(final String sub_mchid,final String openid) throws Exception{
    return addReceivers(sub_mchid,"PERSONAL_OPENID",openid);
  }

  /**
   * 删除分账接收方
   * @param sub_mchid 微信支付分配的子商户号，即分账的出资商户号。
   * @param receiveType 分账接收方类型,和下面的account对应,MERCHANT_ID：商户ID或PERSONAL_OPENID：个人openid
   * @param account 分账接收方账号,和上面的receiveType对应,类型是MERCHANT_ID时，是商户号 类型是PERSONAL_OPENID时，是个人openid
   * @return StringEntity
  */
  private static String delReceivers(final String sub_mchid,final String receiveType,final String account) throws Exception{
    final HttpPost httpPost = getHttpPost(url_receivers_delete,getDelReceivers(sub_mchid,receiveType,account));
    final CloseableHttpResponse response = getHttpClient().execute(httpPost);
    return EntityUtils.toString(response.getEntity());
  }

  /**
   * 删除分账接收方为商户类型
   * @param sub_mchid 微信支付分配的子商户号，即分账的出资商户号。
   * @param mchid 分账方为商户类型时的商户号,可以和‘分账的出资商户号’一致，表示是出资商户号也参与分账
  */
  public static String delMerchantReceiver(final String sub_mchid,final String mchid) throws Exception{
    return delReceivers(sub_mchid,"MERCHANT_ID",mchid);
  }

  /**
   * 删除分账接收方为个人类型
   * @param sub_mchid 微信支付分配的子商户号，即分账的出资商户号。
   * @param openid 分账方为个人时的openId
  */
  public static String delPersonalReceiver(final String sub_mchid,final String openid) throws Exception{
    return delReceivers(sub_mchid,"PERSONAL_OPENID",openid);
  }

  /**
   * 添加请求分账,服务商代特约商户发起分账请求，将结算后的资金分到分账接收方;请求分账到各个接收方（对同一笔订单最多能发起50次分账请求，每次请求最多分给50个接收方）
   * @param sub_mchid 子商户号，即分账的出资商户号。
   * @param transaction_id 微信支付订单号
   * @param out_order_no 商户分账单号
   * @param unfreeze_unsplit 是否解冻剩余未分资金：如果为true，该笔订单剩余未分账的金额会解冻回分账方商户；如果为false，该笔订单剩余未分账的金额不会解冻回分账方商户，可以对该笔订单再次进行分账。
   * @param receivers list分账接收方列表,其中的key为 type(String,分账接收方类型)、account(String,分账接收方账号)、amount(Integer,分账金额,单位为分，只能为整数,需要x100)、description(String，分账描述)
  */
  public static String addSubAccount(final String sub_mchid,final String transaction_id,final String out_order_no,final ArrayList<HashMap<String,Object>> receivers,final boolean unfreeze_unsplit) throws Exception{
    final HttpPost httpPost = getHttpPost(url_profit_share,getSubAccount(sub_mchid,transaction_id,out_order_no,receivers,unfreeze_unsplit));
    final CloseableHttpResponse response = getHttpClient().execute(httpPost);
    return EntityUtils.toString(response.getEntity());
  }
}